using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml.Linq;

using RegexEditor.Properties;

namespace RegexEditor
{
	/// <summary>
	/// A regular expression document that can be reused within the editor.
	/// </summary>
	public class SavedExpression
	{
		#region Constructors

		/// <summary>
		/// Creates a new empty <see cref="Expression"/> class instance.
		/// </summary>
		public SavedExpression()
		{
			ExpressionId = Guid.Empty;
			Pattern = string.Empty;
			TextToMatch = string.Empty;
			ReplacePattern = string.Empty;
		}

		#endregion

		#region Public Members

		/// <summary>
		/// Gets the expression unique ID.
		/// </summary>
		public Guid ExpressionId { get; private set; }

		/// <summary>
		/// Gets or sets the pattern string.
		/// </summary>
		public string Pattern { get; set; }

		/// <summary>
		/// Gets or sets the text to match string.
		/// </summary>
		public string TextToMatch { get; set; }

		/// <summary>
		/// Gets or sets the replacement pattern string.
		/// </summary>
		public string ReplacePattern { get; set; }

		/// <summary>
		/// Gets or sets the regex options.
		/// </summary>
		public RegexOptions Options { get; set; }

		/// <summary>
		/// Gets or sets the expression title.
		/// </summary>
		public string Title { get; set; }

		/// <summary>
		/// Gets or sets whether the expression is matching one string per line within the editor.
		/// </summary>
		public bool OneStringPerLine { get; set; }

		/// <summary>
		/// Gets the list of currently persisted expression documents.
		/// </summary>
		public static IEnumerable<SavedExpression> SavedExpressions
		{
			get
			{
				if (!string.IsNullOrEmpty(Settings.Default.SavedExpressions))
				{
					XDocument expressions = XDocument.Parse(Settings.Default.SavedExpressions);

					foreach (XElement savedExpression in expressions.Root.Elements())
					{
						SavedExpression expression = SavedExpression.FromXmlElement(savedExpression);
						if (expression != null)
							yield return expression;
					}
				}
			}
		}

		/// <summary>
		/// Gets the date of the last update to this document.
		/// </summary>
		public DateTime LastUpdate { get; private set; }

		/// <summary>
		/// Clears all currently saved expressions.
		/// </summary>
		public static void ClearAll()
		{
			Settings.Default.SavedExpressions = null;
		}

		/// <summary>
		/// Clones the current instance.
		/// </summary>
		/// <returns>The cloned instance.</returns>
		public SavedExpression Clone()
		{
			return FromXmlElement(ToXElement());
		}

		/// <summary>
		/// Duplicates this expression.
		/// </summary>
		public SavedExpression Duplicate()
		{
			Save();
			ExpressionId = Guid.NewGuid();
			Title = string.Format("Copy of {0}", Title);
			return this;
		}

		/// <summary>
		/// Deserializes an expression document from an XML string.
		/// </summary>
		/// <param name="xmlString">The XML string.</param>
		/// <returns>The expression document instance.</returns>
		public static SavedExpression FromXmlString(string xmlString)
		{
			if (!string.IsNullOrEmpty(xmlString))
			{
				XDocument doc = XDocument.Parse(xmlString);
				return FromXmlElement(doc.Root);
			}

			return null;
		}

		/// <summary>
		/// Parses an expression document from XML.
		/// </summary>
		/// <param name="element">The XML element.</param>
		/// <returns>The expression document instance,</returns>
		public static SavedExpression FromXmlElement(XElement element)
		{
			try
			{
				if (element != null && element.Name == "Expression")
					return new SavedExpression
					{
						ExpressionId = Guid.Parse(element.Element("Id").Value),
						Title = element.Element("Title").Value,
						Pattern = element.Element("Pattern").Value,
						ReplacePattern = element.Element("ReplacePattern").Value,
						TextToMatch = element.Element("TextToMatch").Value,
						Options = (RegexOptions)Convert.ToInt32(element.Element("RegexOptions").Value),
						OneStringPerLine = Convert.ToBoolean(element.Element("MatchOneStringPerLine").Value),
						LastUpdate = DateTime.Parse(element.Element("LastUpdate").Value)
					};
			}
			catch (Exception)
			{
			}

			return null;
		}

		/// <summary>
		/// Removes the current expression from the saved expressions.
		/// </summary>
		public void Remove()
		{
			if(ExpressionId != Guid.Empty)
			{
				XDocument expressions = new XDocument(new XElement("Expressions"));

				foreach (SavedExpression expression in SavedExpressions)
					if (expression.ExpressionId == ExpressionId)
						continue;
					else
						expressions.Root.Add(expression.ToXElement());

				Settings.Default.SavedExpressions = expressions.ToString();
			}
		}

		/// <summary>
		/// Saves the current expression as a reusable document.
		/// </summary>
		public void Save()
		{
			if (ExpressionId == Guid.Empty)
				ExpressionId = Guid.NewGuid();

			LastUpdate = DateTime.Now;
			bool hasUpdatedExistingItem = false;

			XDocument expressions = new XDocument(new XElement("Expressions"));

			foreach (SavedExpression expression in SavedExpressions)
				if (expression.ExpressionId == ExpressionId)
				{
					expressions.Root.Add(ToXElement());
					hasUpdatedExistingItem = true;
				}
				else
					expressions.Root.Add(expression.ToXElement());

			if (!hasUpdatedExistingItem)
				expressions.Root.Add(ToXElement());

			Settings.Default.SavedExpressions = expressions.ToString();
		}

		/// <summary>
		/// Converts this expression document to XML.
		/// </summary>
		/// <returns>The XML element instance.</returns>
		public XElement ToXElement()
		{
			return new XElement("Expression",
				new XElement("Id", ExpressionId),
				new XElement("Title", Title),
				new XElement("Pattern", Pattern),
				new XElement("ReplacePattern", ReplacePattern),
				new XElement("TextToMatch", TextToMatch),
				new XElement("RegexOptions", (int)Options),
				new XElement("MatchOneStringPerLine", OneStringPerLine),
				new XElement("LastUpdate", LastUpdate)
			);
		}

		#endregion
	}
}
